/*
 * userCashRegister.h
 *
 *  Created on: Jun 16, 2011
 *      Author: peckb1
 */

#ifndef USERCASHREGISTER_H_
#define USERCASHREGISTER_H_

#include "syscall.h"
#include "userItem.h"
#include "userLocks.h"

typedef struct CashRegister {
    int tillBalance;    /* current balance in the till */
    int currentCustomerBalance; /* the current balance the customer being */
                                /* helped has accrued */
    int currentClerkId; /* the id of the clerk using me */
    int currentItemCount; /* the number of items for the customer being helped on me */
    int myId; /* My current Id */
    int currentCustomerId; /* Customer that is being helped by currentClerkId on me */
} CashRegister;

void CashRegisterDoWork(int registerId, int clerkId) {
	int scan;
	int registerStatusLock;
	int registerInteractionLock;
	int peopleWaitingCashLock;
	int registerStatusCV;
	int tmp;
	int registerLock;
	int scanning;
	int returning;
	UserItem itemPurchased;
	UserItem itemRefunded;
	CashRegister cashRegister;
    cashRegister.myId = registerId;
    cashRegister.currentClerkId = clerkId;
    cashRegister.tillBalance = 0;
    cashRegister.currentCustomerBalance = 0;
    cashRegister.currentItemCount = 0;
    scan = TRUE;
    registerStatusLock = theLocksInHomeDepotEntry._registerLock[registerId];
    registerInteractionLock = theLocksInHomeDepotEntry._fromRegisterInteractionLock[registerId];
    peopleWaitingCashLock = theLocksInHomeDepotEntry._peopleWaitingForCashRegiterLock[registerId];
    registerStatusCV = theLocksInHomeDepotEntry._registerCV[registerId];
    /* printf_3("MY REGISTER CV ID = ", theLocksInHomeDepotEntry._registerCV[registerId], "\n"); */
    Acquire(registerStatusLock);
    theLocksInHomeDepotEntry._managerEmtpyingTill[registerId] = -1;

    while(TRUE==TRUE) {
        if(theLocksInHomeDepotEntry._peopleWaitingForCashRegister[registerId] > 0) {
            #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
            printf_5("CASH REGISTER #", registerId, " realizes there are ", theLocksInHomeDepotEntry._peopleWaitingForCashRegister[registerId], " people waiting for it. let one to start\n");
            #endif
            (theLocksInHomeDepotEntry._peopleWaitingForCashRegister[registerId])--;
            if (theLocksInHomeDepotEntry._clerkWantsToInteract[registerId] == 0)
            	theLocksInHomeDepotEntry._clerkWantsToInteract[registerId] = 1;
            else if (theLocksInHomeDepotEntry._managerWantsToInteract[registerId] == 0)
            	theLocksInHomeDepotEntry._managerWantsToInteract[registerId] = 1;
            Signal(theLocksInHomeDepotEntry._peopleWaitingForCashRegiterCV[registerId], registerStatusLock);
        }
        #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
            printf_3("CASH REGISTER #", registerId, " waiting for people to start interacting\n");
        #endif

        Wait(registerStatusCV, registerStatusLock);

        #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
            printf_3("CASH REGISTER #", registerId, " Just got a signal\n");
        #endif

        if(theLocksInHomeDepotEntry._managerEmtpyingTill[registerId] == 0) {
            Release(registerStatusLock);

            #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                printf_3("CASH REGISTER #", registerId, " start interacting with a clerk\n");
            #endif

			#ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                printf_5("CASH REGISTER #", registerId, " Acquiring register interaction lock# ", registerInteractionLock, "\n");
            #endif

            Acquire(registerInteractionLock);
			#ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                printf_5("CASH REGISTER #", registerId, " Acquiring register interaction lock# ", registerInteractionLock, "...Done!\n");
            #endif

            #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                printf_3("CASH REGISTER #", registerId, " clerk wants to scan items\n");
            #endif

            /* InteractWithClerk(cashRegister); */
                scanning = TRUE;
                /* reset totals */
                cashRegister.currentItemCount = 0;
                cashRegister.currentCustomerBalance = 0;
                /* wake up clerk and tell him we're ready for items */
                registerLock = theLocksInHomeDepotEntry._fromRegisterInteractionLock[registerId];

                #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                    printf_3("CASH REGISTER #", registerId, " let the clerk know we are ready for next item\n");
                #endif

                Signal(theLocksInHomeDepotEntry._fromRegisterInteractionCV[registerId], registerLock);

                #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                    printf_3("CASH REGISTER #", registerId, " wait till next item be scanned\n");
                #endif

                Wait(theLocksInHomeDepotEntry._toRegisterInteractionCV[registerId], registerLock);

                #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                    printf_3("CASH REGISTER #", registerId, " clerk just woke us up\n");
                #endif

                itemPurchased = theLocksInHomeDepotEntry._itemToScan[registerId];
                itemRefunded = theLocksInHomeDepotEntry._itemToRefund[registerId];
                returning = FALSE;

                while(itemPurchased._itemId != NOT_FOUND || itemRefunded._itemId != NOT_FOUND) {
                    if(itemPurchased._itemId != NOT_FOUND) {
                        #ifdef PRINT_OUTPUT_CASH_REGISTER
                            printf_7("Cash Register [", registerId, "] got item [", itemPurchased._itemId, "] from Clerk [", clerkId, "] to scan.\n");
                        #endif

                        cashRegister.currentCustomerBalance += itemPurchased._price;
                        theLocksInHomeDepotEntry._itemToScan[registerId]._itemId = NOT_FOUND;
                        cashRegister.currentItemCount++;
                    } else if (itemRefunded._itemId != NOT_FOUND) {
                        returning = TRUE;

                        #ifdef PRINT_OUTPUT_CASH_REGISTER
                            printf_7("Cash Register [", registerId, "] got item [", itemRefunded._itemId, "] from Clerk [", clerkId, "] to scan.\n");
                        #endif

                        cashRegister.currentCustomerBalance -= itemRefunded._price;
                        theLocksInHomeDepotEntry._itemToRefund[registerId]._itemId = NOT_FOUND;
                        cashRegister.currentItemCount++;
                    }
                    #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                        printf_3("CASH REGISTER #", registerId, " let the clerk know we are ready for next item\n");
                    #endif

                    Signal(theLocksInHomeDepotEntry._fromRegisterInteractionCV[registerId], registerLock);

                    #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                        printf_3("CASH REGISTER #",  registerId, " wait till next item be scanned\n");
                    #endif

                    Wait(theLocksInHomeDepotEntry._toRegisterInteractionCV[registerId], registerLock);

                    #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                        printf_3("CASH REGISTER #", registerId, " clerk just woke us up\n");
                    #endif

                    itemPurchased = theLocksInHomeDepotEntry._itemToScan[registerId];
                    itemRefunded = theLocksInHomeDepotEntry._itemToRefund[registerId];
                }
                /* All (or no) items have been scanned. Lets put the totals into */
                /* the shared variable and let the clerk know that he can grab it */
                #ifdef PRINT_OUTPUT_CASH_REGISTER
                    printf_7("Cash Register [", registerId, "] has scanned all [", cashRegister.currentItemCount, "] items for Customer [", theLocksInHomeDepotEntry._customerBeingHelped[registerId] , "].\n");
                #endif

                theLocksInHomeDepotEntry._moneyToTransfer[registerId] = cashRegister.currentCustomerBalance;

                #ifdef PRINT_OUTPUT_CASH_REGISTER
                    printf_11("Cash Register [", registerId, "] gave total cost $[", cashRegister.currentCustomerBalance, "] for Customer [", theLocksInHomeDepotEntry._customerBeingHelped[registerId] , "] to Clerk[", registerId, "] for [", cashRegister.currentItemCount, "] items.\n");
                #endif

                Signal(theLocksInHomeDepotEntry._fromRegisterInteractionCV[registerId], registerLock);

                #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                    printf_3("CASH REGISTER #", registerId , " lets wait till we receive/pay the money\n");
                #endif

                Wait(theLocksInHomeDepotEntry._toRegisterInteractionCV[registerId], registerLock);

                #ifdef PRINT_OUTPUT_CASH_REGISTER
                    if(returning==TRUE) {
                        printf_5("Cash Register [", registerId, "] gave the money to Clerk [", registerId, "].\n");
                    } else {
                        printf_5("Cash Register [", registerId, "] got the money from Clerk [", registerId, "].\n");
                    }
                #endif

                /* Customer paid/received the money, lets print receipt */
                cashRegister.tillBalance += cashRegister.currentCustomerBalance;

                #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                    printf_3("CASH REGISTER #", registerId, " is printing receipt.\n");
                #endif

                Signal(theLocksInHomeDepotEntry._fromRegisterInteractionCV[registerId], registerLock);
				#ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                    printf_3("CASH REGISTER #", registerId, " just signaled.\n");
                #endif

                #ifdef PRINT_OUTPUT_CASH_REGISTER
                    printf_7("Cash Register [", registerId, "] gave receipt to Clerk [", registerId, "] for Customer[", theLocksInHomeDepotEntry._customerBeingHelped[registerId], "].\n");
                #endif
                #ifdef PRINT_OUTPUT_CASH_REGISTER
                    printf_5("Cash Register [", registerId, "] total sale is $[", (theLocksInHomeDepotEntry.registerSales[registerId])+cashRegister.tillBalance, "].\n");
                #endif

			#ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                printf_5("CASH REGISTER #", registerId, " releasing register interaction lock ",registerInteractionLock, "\n");
			#endif
            Release(registerInteractionLock);
			#ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                printf_5("CASH REGISTER #", registerId, " releasing register interaction lock ",registerInteractionLock, "..Done!\n");
			#endif

        } else if(theLocksInHomeDepotEntry._managerEmtpyingTill[registerId] == 1) {
            Release(registerStatusLock);

            #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                printf_3("CASH REGISTER #", registerId, " start interacting with manager\n");
            #endif

            Acquire(registerInteractionLock);

            #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                printf_3("CASH REGISTER #", registerId, " manager wants to empty till \n");
            #endif

            /* InteractWithManager(cashRegister); */
                /* wake up the manager and tell him the total of the till */
                registerLock = theLocksInHomeDepotEntry._fromRegisterInteractionLock[registerId];

                #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                    printf_5("CASH REGISTER #", registerId, " let the manager know that total till is ", cashRegister.tillBalance, "\n");
                #endif

                theLocksInHomeDepotEntry._moneyToTransfer[registerId] = cashRegister.tillBalance;
                Signal(theLocksInHomeDepotEntry._fromRegisterInteractionCV[registerId], registerLock);

                #ifdef PRINT_EXTRA_LOG_CASH_REGISTER
                    printf_3("CASH REGISTER #", registerId, " wait till manager picks up the till content\n");
                #endif

                Wait(theLocksInHomeDepotEntry._toRegisterInteractionCV[registerId], registerLock);

                /* Increase Sales */
                (theLocksInHomeDepotEntry.registerSales[registerId]) += cashRegister.tillBalance;
                cashRegister.tillBalance = 0;

                #ifdef PRINT_OUTPUT_CASH_REGISTER
                    printf_3("Cash Register [", registerId, "] drawer has emptied by Manager.\n");
                #endif
                #ifdef PRINT_OUTPUT_CASH_REGISTER
                    printf_5("Cash Register [", registerId, "] total sale until now is $[", (theLocksInHomeDepotEntry.registerSales[registerId]) , "].\n");
                #endif
            Release(registerInteractionLock);
        }
        Acquire(registerStatusLock);
        theLocksInHomeDepotEntry._managerEmtpyingTill[registerId] = -1;
        for(tmp = 0; tmp < 10; tmp++) {
            Yield();
        }

    }
    Release(registerStatusLock);
}

#endif /* USERCASHREGISTER_H_ */
